﻿using System;
using System.Windows.Forms;
using System.Security;
using System.Text;
using System.Runtime.InteropServices;
using PowerTriggers.ServiceReference;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;

namespace PowerTriggers
{
    internal interface IHistoryItemTreeNode
    {
        IHistoryItem HistoryItem { get; }

        string Key { get; }

        HistoryItemTypes Type { get; }
    }

    internal class HistoryTreeView : TreeView, IPowerTriggersWcfCallbackCallback
    {
        private ImageList imageList1;
        private System.ComponentModel.IContainer components;
        private HistoryData _historyData;

        public HistoryTreeView()
            : base()
        {
            InitializeComponent();

            Bitmap blankBitmap = new Bitmap(16, 16);
            using (Graphics g = Graphics.FromImage(blankBitmap))
            {
                g.Clear(Color.White);
            }

            imageList1.Images.Add(HistoryItemImageTypes.None.ToString(), blankBitmap);
            imageList1.Images.Add(HistoryItemImageTypes.Informational.ToString(), global::PowerTriggers.Properties.Resources.Informational);
            imageList1.Images.Add(HistoryItemImageTypes.Success.ToString(), global::PowerTriggers.Properties.Resources.Success);
            imageList1.Images.Add(HistoryItemImageTypes.Warning.ToString(), global::PowerTriggers.Properties.Resources.Warning);
            imageList1.Images.Add(HistoryItemImageTypes.Error.ToString(), global::PowerTriggers.Properties.Resources.Error);
        }

        private void RefreshData(HistoryData historyData)
        {
            if (historyData == null || historyData.History.Length == 0)
            {
                this._historyData = historyData;
                this.Nodes.Clear();
            }
            else
            {
                bool hasHistoryChanged;
                if (this._historyData == null)
                {
                    hasHistoryChanged = true;
                }
                else if (this._historyData.History.Length == historyData.History.Length)
                {
                    hasHistoryChanged = false;
                    Dictionary<string, HistoryHeader> previousHeaders = this._historyData.History.ToDictionary(h => h.Key, h => h);
                    foreach (HistoryHeader header in historyData.History)
                    {
                        if (!previousHeaders.ContainsKey(header.Key))
                        {
                            hasHistoryChanged = true;
                            break;
                        }
                    }

                    if (!hasHistoryChanged)
                    {
                        Dictionary<string, HistoryDetail> previousDetails = this._historyData.History.SelectMany(h => h.Details).ToDictionary(d => d.Key, d => d);
                        foreach (HistoryDetail detail in historyData.History.SelectMany(h => h.Details))
                        {
                            if (!previousDetails.ContainsKey(detail.Key))
                            {
                                hasHistoryChanged = true;
                                break;
                            }
                        }
                    }
                }
                else
                {
                    hasHistoryChanged = true;
                }

                this._historyData = historyData;

                if (hasHistoryChanged)
                {
                    List<HistoryHeaderTreeNode> expandedNodes = this.Nodes
                                                    .OfType<HistoryHeaderTreeNode>()
                                                    .Where(node => node.IsExpanded)
                                                    .Select(node => node)
                                                    .ToList();
                    bool isLastSelected = false;
                    IHistoryItemTreeNode selectedHistoryItem = null;
                    if (this.SelectedNode != null)
                    {
                        if (!this.SelectedNode.IsExpanded)
                        {
                            if (this.SelectedNode.NextNode == null)
                            {
                                isLastSelected = true;
                            }
                        }
                        if (!isLastSelected)
                        {
                            selectedHistoryItem = (IHistoryItemTreeNode)this.SelectedNode;
                        }
                    }

                    try
                    {
                        this.BeginUpdate();

                        HistoryHeaderTreeNode[] parentNodes = this._historyData.History
                                                                .Select(item => new HistoryHeaderTreeNode(item, this.imageList1))
                                                                .ToArray();
                        this.Nodes.Clear();
                        this.Nodes.AddRange(parentNodes);

                        foreach (HistoryHeaderTreeNode expandedNode in expandedNodes)
                        {
                            if (this.Nodes.ContainsKey(expandedNode.Key))
                            {
                                this.Nodes[expandedNode.Key].Expand();
                            }
                        }

                        if (isLastSelected)
                        {
                            this.SelectedNode = this.LastNode;
                        }
                        else if (selectedHistoryItem != null)
                        {
                            bool found = false;
                            if (selectedHistoryItem.Type == HistoryItemTypes.Header)
                            {
                                if (this.Nodes.ContainsKey(selectedHistoryItem.Key))
                                {
                                    this.SelectedNode = this.Nodes[selectedHistoryItem.Key];
                                    found = true;
                                }
                            }
                            else
                            {
                                foreach (HistoryHeaderTreeNode node in this.Nodes)
                                {
                                    if (node.Nodes.ContainsKey(selectedHistoryItem.Key))
                                    {
                                        this.SelectedNode = this.Nodes[selectedHistoryItem.Key];
                                        found = true;
                                        break;
                                    }
                                }
                            }
                            if (!found)
                            {
                                this.SelectedNode = this.FirstNode;
                            }
                        }
                        else
                        {
                            this.SelectedNode = this.LastNode;
                        }
                    }
                    finally
                    {
                        this.EndUpdate();
                        // this call needs to be deferred until after the call to EndUpdate because it doesn't work within a BeginUpdate ... EndUpdate block
                        this.EnsureLastNodeIsVisible();
                    }
                }
            }
        }

        internal TreeNode FirstNode
        {
            get
            {
                return this.Nodes.OfType<TreeNode>().FirstOrDefault();
            }
        }

        internal TreeNode LastNode
        {
            get
            {
                TreeNode lastNode = this.Nodes.OfType<TreeNode>().LastOrDefault();
                if (lastNode != null && lastNode.IsExpanded)
                {
                    lastNode = lastNode.Nodes.OfType<TreeNode>().LastOrDefault();
                }

                return lastNode;
            }
        }

        internal void EnsureLastNodeIsVisible()
        {
            TreeNode lastNode = this.LastNode;

            if (lastNode != null)
            {
                lastNode.EnsureVisible();
            }
        }

        internal string ConvertToText()
        {
            StringBuilder sb = new StringBuilder();

            HistoryHeaderTreeNode parentNode = (HistoryHeaderTreeNode)this.FirstNode;
            while (parentNode != null)
            {
                sb.AppendLine(parentNode.HistoryItem.LogFileText);
                HistoryDetailTreeNode childNode = (HistoryDetailTreeNode)parentNode.FirstNode;
                while (childNode != null)
                {
                    sb.AppendLine(childNode.HistoryItem.LogFileText);
                    childNode = (HistoryDetailTreeNode)childNode.NextNode;
                }
                parentNode = (HistoryHeaderTreeNode)parentNode.NextNode;
            }

            return sb.ToString();
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.imageList1 = new System.Windows.Forms.ImageList(this.components);
            this.SuspendLayout();
            // 
            // imageList1
            // 
            this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
            this.imageList1.ImageSize = new System.Drawing.Size(16, 16);
            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
            // 
            // HistoryTreeView
            // 
            this.ImageIndex = 0;
            this.ImageList = this.imageList1;
            this.LineColor = System.Drawing.Color.Black;
            this.SelectedImageIndex = 0;
            this.ResumeLayout(false);

        }

        #region IPowerTriggersWcfCallbackCallback Members

        void IPowerTriggersWcfCallbackCallback.HistoryChanged(HistoryData historyData)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<HistoryData>(this.RefreshData));
            }
            else
            {
                this.RefreshData(historyData);
            }
        }
        #endregion
    }

    internal class HistoryHeaderTreeNode : TreeNode, IHistoryItemTreeNode
    {
        private HistoryHeaderTreeNode() { }

        internal HistoryHeaderTreeNode(HistoryHeader header, ImageList imageList)
        {
            HistoryHeader = header;
            this.Text = header.ToString();
            this.Name = header.Key;

            if (header.Details != null && header.Details.Length > 0)
            {
                this.Nodes.AddRange(header.Details.Select(d => new HistoryDetailTreeNode(d, imageList)).ToArray());
            }

            switch (header.ImageType)
            {
                case HistoryItemImageTypes.Informational:
                case HistoryItemImageTypes.Success:
                case HistoryItemImageTypes.Warning:
                case HistoryItemImageTypes.Error:
                    this.ImageKey = header.ImageType.ToString();
                    this.SelectedImageKey = this.ImageKey;
                    break;
                default:
                    break;
            }
        }

        internal HistoryHeader HistoryHeader { get; private set; }

        #region IHistoryItemTreeNode
        public IHistoryItem HistoryItem
        {
            get { return this.HistoryHeader; }
        }

        public string Key
        {
            get { return this.HistoryItem.Key; }
        }

        public HistoryItemTypes Type
        {
            get { return this.HistoryItem.Type; }
        }
        #endregion
    }

    internal class HistoryDetailTreeNode : TreeNode, IHistoryItemTreeNode
    {
        private HistoryDetailTreeNode() { }

        internal HistoryDetailTreeNode(HistoryDetail detail, ImageList imageList)
        {
            HistoryDetail = detail;
            this.Text = detail.ToString();
            this.Name = detail.Key;

            switch (detail.ImageType)
            {
                case HistoryItemImageTypes.Success:
                case HistoryItemImageTypes.Warning:
                case HistoryItemImageTypes.Error:
                    this.ImageKey = detail.ImageType.ToString();
                    this.SelectedImageKey = this.ImageKey;
                    break;
                default:
                    break;
            }
        }

        internal HistoryDetail HistoryDetail { get; private set; }

        #region IHistoryItemTreeNode
        public IHistoryItem HistoryItem
        {
            get { return this.HistoryDetail; }
        }

        public string Key
        {
            get { return this.HistoryItem.Key; }
        }

        public HistoryItemTypes Type
        {
            get { return this.HistoryItem.Type; }
        }
        #endregion
    }
}
